package util

import (
	"database/sql/driver"
	"fmt"
	"pay/pkg/ecode"
	"strings"
	"time"
)

const (
	// 默认
	DefaultLayout = "2006-01-02 15:04:05"
	// 无时区有T
	TDefaultLayout = "2006-01-02T15:04:05"
	// 有时区
	DateTimeWithTimezone = "2006-01-02 15:04:05 -0700"
	// 有时区有T
	TDateTimeWithTimezone = "2006-01-02T15:04:05 -0700"
	// 有时区
	RFC3339 = "2006-01-02T15:04:05-07:00"
	// 精确到微秒
	DefaultLayoutWithMS = "2006-01-02 15:04:05.370917"
	// 精确到微秒有T
	TDefaultLayoutWithMS = "2006-01-02T15:04:05.370917"
	// 包含时区信息+微秒
	DefaultLayoutWithMSAndTimezone = "2006-01-02 15:04:05.370917 -0700 UTC"
	// 精确到分钟
	MinuteLayout = "2006-01-02 15:04"
	// 日期
	DateLayout = "2006-01-02"
	// 月
	MonthLayout = "2006-01"
	// 时
	DurationLayout = "15:04:05"
	// 分
	DurationWithoutSecondLayout = "15:04"
	// 数值时间
	NumberLayout = "20060102150405"
	//// clickhouse optimize 月份
	//MonthLayoutWithoutSlash = "200601"
)

var FixedLocation *time.Location

func init() {
	FixedLocation = time.FixedZone("CST", 8*3600)
}

func ParseTimeWithZero(timeString string) (_t time.Time) {
	var err error
	if _t, err = ParseTime(timeString); err != nil {
		_t = time.Time{}
	}
	return
}

func ParseTime(timeString string) (_t time.Time, err error) {
	switch len(timeString) {
	case len(DefaultLayout):
		// 无时区http入参解析模式
		if timeString[10] == 'T' {
			_t, err = time.ParseInLocation(TDefaultLayout, timeString, FixedLocation)
		} else {
			_t, err = time.ParseInLocation(DefaultLayout, timeString, FixedLocation)
		}
	case len(DateTimeWithTimezone):
		// 无时区http入参解析模式
		if timeString[10] == 'T' {
			_t, err = time.ParseInLocation(TDateTimeWithTimezone, timeString, FixedLocation)
		} else {
			_t, err = time.ParseInLocation(DateTimeWithTimezone, timeString, FixedLocation)
		}
	case len(DefaultLayoutWithMS):
		if timeString[10] == 'T' {
			_t, err = time.ParseInLocation(TDefaultLayoutWithMS, timeString, FixedLocation)
		} else {
			_t, err = time.ParseInLocation(DefaultLayoutWithMS, timeString, FixedLocation)
		}
	case len(MinuteLayout):
		_t, err = time.ParseInLocation(MinuteLayout, timeString, FixedLocation)
	case len(DateLayout):
		// 仅日期
		_t, err = time.ParseInLocation(DateLayout, timeString, FixedLocation)
	case len(MonthLayout):
		// 仅月份
		_t, err = time.ParseInLocation(MonthLayout, timeString, FixedLocation)
	case len(DefaultLayoutWithMSAndTimezone):
		_t, err = time.ParseInLocation(DefaultLayoutWithMSAndTimezone, timeString, FixedLocation)
	default:
		err = ecode.WithStack(fmt.Errorf("时间格式有误，input=%s", timeString))
	}
	return
}

// 可空时间
type NullTime struct {
	Time  time.Time
	Valid bool // Valid is true if Time is not NULL
}

// Scan implements the Scanner interface.
func (n *NullTime) Scan(value interface{}) error {
	if value == nil {
		n.Time, n.Valid = time.Time{}, false
		return nil
	}
	n.Valid = true
	var err error
	switch value.(type) {
	case []byte:
		n.Time, err = ParseTime(string(value.([]byte)))
	case string:
		n.Time, err = ParseTime(value.(string))
	case time.Time:
		n.Time = value.(time.Time)
	default:
		err = ecode.BadRequest(fmt.Sprint("Failed to convert value to time:", value))
	}
	return err
}

// Value implements the driver Valuer interface.
func (n NullTime) Value() (driver.Value, error) {
	if !n.Valid {
		return nil, nil
	}
	return n.Time, nil
}

// Duration custom time like "15:04:05"
type Duration time.Time

// Scan  implement sql.Scanner interface，Scan convert value to time.Duration.
func (t *Duration) Scan(value interface{}) error {
	timeString, ok := value.(string)
	if !ok {
		return ecode.BadRequest(fmt.Sprint("Failed to convert string value:", value))
	}
	_t, err := time.Parse(DurationLayout, timeString)
	*t = Duration(_t)
	return err
}

// Value implement driver.Valuer interface，return string value.
func (t Duration) Value() (driver.Value, error) {
	return time.Time(t).Format(DurationLayout), nil
}

// Value implement driver.Valuer interface，return string value.
func (t Duration) String() string {
	return time.Time(t).Format(DurationLayout)
}

// MarshalJSON custom time format.
func (t Duration) MarshalJSON() ([]byte, error) {
	return []byte(`"` + time.Time(t).Format(DurationLayout) + `"`), nil
}

// UnmarshalJSON custom unmarshal Duration.
func (t *Duration) UnmarshalJSON(buf []byte) error {
	_t, err := time.Parse(DurationLayout, strings.Trim(string(buf), `"`))
	if err != nil {
		return err
	}
	*t = Duration(_t)
	return nil
}
